/*
 *	Copyright © 2008 University of Houston
 *	All rights reserved.
 */
#include <iostream>
#include <math.h>
#include <hash_map.h>
#include <map>
#include "resultsfilter.h"
#include "resultscomparator.h"

using namespace std;

resultsFilter::resultsFilter(float p)
{
	parameter = p;
}

vector<analyzerResultList> 
resultsFilter::doFilter(vector<analyzerResultList>* longConns,
								vector<analyzerResultList>* shortConns)
{
	vector<analyzerResultList> empty;
	return empty;
}

analyzerResultList resultsFilter::doFilter(analyzerResultList resultsList)
{
	return resultsList;
};

resultsFilter * resultsFilter::getFilter(char c, float f)
{
	switch(c)
	{
		case 't':	return new sortInOrder(f);
		case 'u':	return new reverseSort(f);
		case 's':	return new smoothResults(f);
		case 'c':	return new chompResults(f);
		case 'm':	return new getMedian(f);
		case 'r':	return new getRepVal(f);
		case 'd':	return new modeResults(f);
		case 'g':	return new separationComparator;
		case 'i':	return new crissCross(f);
	}
	
	// Default
	ERROR("Could not identify resultsFilter \'" << c << "\'")
	return new resultsFilter;
}

analyzerResultList sortInOrder::doFilter(analyzerResultList resultsList)
{
	sort(resultsList.begin(), resultsList.end());
	return resultsList;
};

analyzerResultList reverseSort::doFilter(analyzerResultList resultsList)
{
	analyzerResultList a;
	a.initVals(resultsList);
	sortInOrder s;
	a = s.doFilter(resultsList);
	reverse(a.begin(), a.end());
	return a;
};

analyzerResultList smoothResults::doFilter(analyzerResultList resultsList)
{
	analyzerResult 			temp = resultsList[0];
	analyzerResultList		smoothedResults;
	smoothedResults.initVals (resultsList);
	analyzerResultList::iterator	iter;
	
	INFO("Smoothing value: " << parameter);

	for ( iter = resultsList.begin(); iter != resultsList.end();	iter++)
	{
		
		temp += ((*iter) * parameter);
		temp /= (1 + parameter);

		VDEBUG("Iterating over value " << *iter << ", smoothed to " << temp)
		
		smoothedResults.push_back(temp);
	}

	return smoothedResults;
};

analyzerResultList chompResults::doFilter(analyzerResultList resultsList)
{
	uint count = resultsList.size() * parameter;
	if( count > resultsList.size() )
	{
		WARNING("Specified too many items to chomp from results!!"
				" Setting chomp to remove all items from results.")
		count = resultsList.size();
	}
	
	INFO("Chomping off " << parameter*100 << "% [" << count << "] results")
	
	resultsList.erase( resultsList.begin(), resultsList.begin() + count);
	return resultsList;
};

analyzerResultList getMedian::doFilter(analyzerResultList resultsList)
{
	sortInOrder s;
	analyzerResultList sortedResults = s.doFilter(resultsList);
	int size = resultsList.size();
	analyzerResultList out;
	out.initVals(resultsList);
	out.push_back(resultsList[size / 2]); // Store our median value
	return out;
};

analyzerResultList getRepVal::doFilter(analyzerResultList resultsList)
{
	chompResults c; // Chomp off first 50% of resusults
	c.parameter = 0.5f; //FIXME: This is an ugly way of doing this
	//chompResults c(0.5f); //does not work..?

	analyzerResultList lastHalf = c.doFilter(resultsList);
	getMedian med;
	analyzerResultList out;
	return med.doFilter(lastHalf); // Return the median of the last half
};

analyzerResultList modeResults::doFilter(analyzerResultList resultsList)
{
	long double mode 		= -1;
	int			powerOfTen 	= -5;	// Start at 0.00001 precision
	
	while( (mode = findMode(&resultsList)) == -1 && powerOfTen < 10)
	{
		INFO("Rounding data down to 10e" << powerOfTen << " place")
		roundTo(powerOfTen, &resultsList);
		powerOfTen++;
	}
	
	analyzerResultList returnValue;
	returnValue.initVals(resultsList);
	if( mode != -1 )
		returnValue.push_back(mode);
	else
		ERROR("Could not find mode for resultList!")
		
	INFO("Found mode value: " << mode)
		
	return returnValue;
}

void modeResults::roundTo(int powerOfTen, analyzerResultList *p)
{
	// -=====- Example -=====-
	// value 		= 0.05464
	// powerOfTen 	= -3
	// multiplier	= 0.001
	// value = value/multiplier = 54.64
	// value = ceiling(value) = 54
	// value = value * multiplier = .054
	long double multiplier = pow(10, powerOfTen);

	analyzerResultList::iterator i;
	for(i = p->begin(); i != p->end(); i++)
	{
		(*i) /= multiplier;
		(*i) = ceil(*i);
		(*i) *= multiplier;
	}
	
}
long double modeResults::findMode(analyzerResultList *p)
{
	map<long double, int> h;
	analyzerResultList::iterator i;

	// Add everything to the map
	for(i = p->begin(); i != p->end(); i++)
	{
		VDEBUG("Checking value " << *i << " to find mode.")
		
		// Is there an entry for this value yet?  Default to zero.
		if( h.find(*i) == h.end() )
			h[*i] = 0;
		
		h[*i]++;
	}

	// Find the most frequent value
	map<long double, int>::iterator hIter;
	long double mostFrequentValue 	= 0;
	int			count 				= 0;
	int			highestCount		= 0;
	for(hIter = h.begin(); hIter != h.end(); hIter++)
	{
		count = (*hIter).second;
		
		VDEBUG("Checking value " << (*hIter).first << 
				" to see if it the highest frequency (" << count << ")")
		if(count > highestCount)
		{
			highestCount 		= count;
			mostFrequentValue 	= (*hIter).first;
		}
	}
	
	
	// Check to see if it occurs frequently enough
	float percentage = (float)highestCount / (float)p->size();
	DEBUG("Found most frequent value " << mostFrequentValue
 			<< " with frequency of " << percentage << "%")
		
	if( percentage > parameter)
		return mostFrequentValue;		
	return -1;
}


vector<analyzerResultList>
separationComparator::doFilter (vector<analyzerResultList>* longConns,
								  vector<analyzerResultList>* shortConns)
{
	analyzerResult	temp 		= 0;
	analyzerResultList *tempPtr = NULL;
	
	// Step one: Find the lowest value in longConns.
	analyzerResult 	lowestLong 	= 0;
	for (uint conn=0; conn < longConns->size(); conn++)
	{
		tempPtr = & (*longConns)[conn];
		temp = findMaximals(tempPtr, true);
		DEBUG("Checking for min: " << temp << "<" << lowestLong << "?")
		if(temp < lowestLong || lowestLong == 0)
			lowestLong = temp;
	}
	VDEBUG("Found minimal long " << temp)

	// Step two: Find the highest value in shortConns
	analyzerResult highestShort = 0;
	temp						= 0;
	for (uint conn=0; conn<shortConns->size(); conn++)
	{
		tempPtr = & (*shortConns)[conn];
		temp = findMaximals(tempPtr, false);
		DEBUG("Checking for max: " << temp << ">" << highestShort << "?")
		if(temp > highestShort || highestShort == 0)
			highestShort = temp;
	}
	VDEBUG("Found maximal short " << temp)
	
	// Return the results as a vector.
	vector<analyzerResultList> retVal;
	analyzerResultList retValList;
	retValList.push_back(highestShort);
	retValList.push_back(lowestLong);
	retVal.push_back(retValList);
	
	INFO("Identified boundaries " << lowestLong << "," << highestShort)
	
	return retVal;
}

analyzerResult
separationComparator::findMaximals(analyzerResultList *results, bool bLow)
{
	bool 					bHigh 		= !bLow;
	analyzerResult 			maximal 	= 0.0f;
	analyzerResultList::iterator 	iter;
	
	// Set the first value.
	maximal = (*results)[0];
	
	// Just iterate through all of the results
	for( iter = results->begin(); iter != results->end(); iter++ )
	{	
		// Are we looking for the lowest?
		if(bLow)
			if( (*iter) < maximal )
				maximal = *iter;
		
		// Or the highest?
		if(bHigh)
			if( (*iter) > maximal )
				maximal = *iter;		
	}
	
	return maximal;
}


vector<analyzerResultList>
crissCross::doFilter (vector<analyzerResultList>* longConns,
								  vector<analyzerResultList>* shortConns)
{
	sortInOrder sio;
	reverseSort rs;
	
	analyzerResultList longCoalesced;
	analyzerResultList shortCoalesced;

	// Copy the results of each individual analyzerResultList into the
	// coalesced list.
	for(uint i = 0; i<longConns->size(); i++)
		longCoalesced.insert(longCoalesced.begin(),
								(*longConns)[i].begin(),
								(*longConns)[i].end());
	for(uint i = 0; i<shortConns->size(); i++)
		shortCoalesced.insert(shortCoalesced.begin(),
								(*shortConns)[i].begin(),
								(*shortConns)[i].end());

	// Sort the results
	longCoalesced = rs.doFilter(longCoalesced);
	longCoalesced.filename = "long";
	longCoalesced.sessionNumber = 0;
	shortCoalesced = sio.doFilter(shortCoalesced);
	shortCoalesced.filename = "short";
	shortCoalesced.sessionNumber = 0;

	// Clear vectors
	longConns->clear();
	shortConns->clear();

	// Reassign the longConns and shortConns
	longConns->push_back(longCoalesced);
	shortConns->push_back(shortCoalesced);
	
	// Return an empty vector
	vector<analyzerResultList> emptyReturnValue;
	return emptyReturnValue;
}


//------------------Results Filter Parser---------------//

vector<resFilterPair> resFilterPair::parseResFilters (string s)
{
	vector<resFilterPair> out;

	if (s.size() == 0)
		return out;

	// Split the string by commas to get filter param pair
	vector<string> roughParse = stringSplit(s, ",");

	string filterCharString;
	char   filterChar;
	float  filterParam;

	for (u_int pair = 0; pair < roughParse.size(); pair++)
	{
		// Split the string by '=' to get the filtname/param
		vector<string> fineParse = stringSplit(roughParse[pair], "=");
		if (fineParse.size() < 1)
			ERROR("Problem Parsing Results Filters!")
		
		filterCharString  = fineParse[0];
		filterChar = filterCharString[0];
		if (fineParse.size() > 1)
			filterParam = strtof(fineParse[1].c_str(), NULL);
		else 
			filterParam = 0;

		resFilterPair rfp(filterChar, filterParam);
		out.push_back(rfp);
	}

	return out;
};

